"""
Yuxi-Know MCP服务器适配器
实现MCP工具功能，调用内部FastAPI接口
"""
import os
import json
import httpx
import logging
from typing import Dict, Any, Optional, List
from datetime import datetime
from functools import lru_cache

from server.mcp.yuxi_know_tools import (
    KnowledgeQueryInput, GraphQueryInput, ChatInput, KnowledgeBaseCreateInput,
    EntitySearchInput, SystemHealthOutput, KnowledgeQueryOutput, GraphDataOutput,
    ChatOutput, AvailableKnowledgeBasesOutput, EntitySearchOutput, ToolListOutput,
    GraphStatsOutput
)

# 日志配置
logger = logging.getLogger(__name__)

# API配置
API_BASE_URL = os.getenv("YUXI_KNOW_API_URL", "http://192.168.3.152:5050/api")
API_KEY_ENABLED = os.getenv("API_KEY_ENABLED", "true").lower() == "true"
THIRD_PARTY_API_KEY = os.getenv("THIRD_PARTY_API_KEY", "")
MCP_REQUEST_TIMEOUT = int(os.getenv("MCP_REQUEST_TIMEOUT", "30"))

# 日志配置
logger = logging.getLogger(__name__)


class YuxiKnowMCPServer:
    """Yuxi-Know MCP服务器适配器"""

    def __init__(self, api_base_url: str = None, api_key: str = None):
        """
        初始化MCP服务器

        Args:
            api_base_url: Yuxi-Know API基础URL
            api_key: API密钥用于认证
        """
        self.api_base_url = api_base_url or API_BASE_URL
        self.api_key = api_key or THIRD_PARTY_API_KEY
        self.timeout = MCP_REQUEST_TIMEOUT

        logger.info(f"初始化Yuxi-Know MCP Server，API地址: {self.api_base_url}")

    async def _make_request(self, method: str, endpoint: str, **kwargs) -> Dict[str, Any]:
        """
        发起HTTP请求到内部API

        Args:
            method: HTTP方法
            endpoint: API端点（不含base_url）
            **kwargs: requests其他参数

        Returns:
            API响应数据
        """
        url = f"{self.api_base_url}{endpoint}"
        headers = {}

        # 添加API Key认证
        if self.api_key and API_KEY_ENABLED:
            headers["X-API-Key"] = self.api_key

        timeout_config = {
            "connect": 10,
            "read": self.timeout,
            "write": self.timeout,
            "pool": self.timeout
        }

        logger.info(f"HTTP请求: {method} {url}")
        if 'params' in kwargs:
            logger.debug(f"请求参数: {kwargs['params']}")

        try:
            async with httpx.AsyncClient(
                timeout=httpx.Timeout(**timeout_config),
                headers=headers
            ) as client:
                response = await client.request(method, url, **kwargs)
                logger.debug(f"收到响应: 状态码 {response.status_code}")

                # 处理响应
                if response.status_code == 200:
                    try:
                        return response.json()
                    except json.JSONDecodeError:
                        logger.error(f"JSON解析失败: {response.text[:200]}")
                        return {"error": "响应数据格式错误", "status_code": response.status_code}

                elif response.status_code == 401:
                    logger.error(f"API认证失败: {url}")
                    return {"error": "API认证失败，请检查API Key配置", "status_code": response.status_code}

                elif response.status_code == 403:
                    logger.error(f"权限不足: {url}")
                    return {"error": "权限不足，MCP接口对该用户不可访问", "status_code": response.status_code}

                elif response.status_code == 429:
                    logger.error(f"频率超限: {url}")
                    return {"error": "API调用频率超限，请稍后再试", "status_code": response.status_code}

                else:
                    error_msg = f"API请求失败: {response.status_code}"
                    try:
                        error_data = response.json()
                        error_msg = error_data.get('detail', error_msg)
                    except:
                        error_msg = response.text[:200] if response.text else error_msg

                    logger.error(f"{error_msg} - URL: {url}")
                    return {"error": error_msg, "status_code": response.status_code}

        except httpx.TimeoutException:
            logger.error(f"请求超时: {url} (timeout: {self.timeout}s)")
            return {"error": f"请求超时，超时时间: {self.timeout}秒", "status_code": 408}

        except httpx.ConnectError as e:
            logger.error(f"连接失败: {url} - {e}")
            return {"error": f"无法连接到Yuxi-Know服务: {self.api_base_url}", "status_code": 503}

        except Exception as e:
            logger.error(f"HTTP请求异常: {url} - {e}")
            return {"error": f"请求处理异常: {str(e)}", "status_code": 500}

    async def check_system_health(self) -> SystemHealthOutput:
        """获取系统健康状态"""
        logger.info("执行MCP工具: yuxi-know-health")

        try:
            result = await self._make_request("GET", "/system/health")

            if result.get("status") == "ok":
                return SystemHealthOutput(
                    status="healthy",
                    message=result.get("message", "系统运行正常"),
                    timestamp=datetime.now().isoformat()
                )
            else:
                return SystemHealthOutput(
                    status="unhealthy",
                    message=result.get("error", "系统异常"),
                    timestamp=datetime.now().isoformat()
                )

        except Exception as e:
            logger.error(f"系统健康检查异常: {e}")
            return SystemHealthOutput(
                status="error",
                message=f"健康检查失败: {e}",
                timestamp=datetime.now().isoformat()
            )

    async def search_knowledge_base(self, input_data: KnowledgeQueryInput) -> KnowledgeQueryOutput:
        """在知识库中查询内容"""
        logger.info(f"执行MCP工具: yuxi-know-query-knowledge (db: {input_data.database_id})")

        try:
            # 构建查询参数
            params = {
                "q": input_data.query,
                "top_k": input_data.top_k,
                "threshold": input_data.threshold
            }

            # 为LightRAG添加检索模式参数
            if input_data.search_type in ["local", "global", "hybrid", "naive", "mix"]:
                params["mode"] = input_data.search_type

            result = await self._make_request("GET", f"/knowledge/databases/{input_data.database_id}/query", params=params)

            if "error" in result:
                return KnowledgeQueryOutput(
                    results=[],
                    count=0,
                    time_taken=0.0,
                    database_name=input_data.database_id,
                )

            # 提取查询结果
            results = []
            if "results" in result:
                for item in result["results"]:
                    results.append({
                        "content": item.get("content", ""),
                        "metadata": item.get("metadata", {}),
                        "similarity_score": item.get("score", 0.0)
                    })

            return KnowledgeQueryOutput(
                results=results,
                count=len(results),
                time_taken=result.get("time_taken", 0.0),
                database_name=input_data.database_id,
            )

        except Exception as e:
            logger.error(f"知识库查询异常: {e}")
            return KnowledgeQueryOutput(
                results=[],
                count=0,
                time_taken=0.0,
                database_name=input_data.database_id,
            )

    async def query_graph(self, input_data: GraphQueryInput) -> GraphDataOutput:
        """查询知识图谱"""
        logger.info(f"执行MCP工具: yuxi-know-query-graph (node: {input_data.node_label})")

        try:
            params = {
                "node_label": input_data.node_label,
                "depth": input_data.depth,
                "max_nodes": input_data.max_nodes
            }

            result = await self._make_request("GET", "/graph/lightrag/subgraph", params=params)

            if "error" in result:
                return GraphDataOutput(
                    nodes=[],
                    edges=[],
                    total_nodes=0,
                    total_edges=0,
                    depth_exceeded=False
                )

            # 提取图谱数据
            nodes = result.get("nodes", [])
            edges = result.get("edges", [])

            return GraphDataOutput(
                nodes=nodes,
                edges=edges,
                total_nodes=len(nodes),
                total_edges=len(edges),
                depth_exceeded=result.get("depth_exceeded", False)
            )

        except Exception as e:
            logger.error(f"图谱查询异常: {e}")
            return GraphDataOutput(
                nodes=[],
                edges=[],
                total_nodes=0,
                total_edges=0,
                depth_exceeded=False
            )

    async def chat_with_intelligent_agent(self, input_data: ChatInput) -> ChatOutput:
        """与智能体对话"""
        logger.info(f"执行MCP工具: yuxi-know-chat (agent: {input_data.agent_id})")

        try:
            # 构建请求数据
            data = {
                "text": input_data.message,
            }

            # 额外的配置参数
            if input_data.thread_id:
                data["thread_id"] = input_data.thread_id
            if input_data.knowledge_base_ids:
                data["knowledge_base_ids"] = input_data.knowledge_base_ids
            if input_data.model_name:
                data["model_name"] = input_data.model_name

            result = await self._make_request("POST", f"/chat/agent/{input_data.agent_id}", json=data)

            if "error" in result:
                return ChatOutput(
                    response=f"对话失败: {result.get('error', '未知错误')}",
                    thread_id=input_data.thread_id or "error_thread",
                    agent_id=input_data.agent_id,
                    timestamp=datetime.now().isoformat(),
                    context_used=False
                )

            # 对于流式响应，我们简化处理，直接返回最后的响应
            if isinstance(result, dict) and "response" in result:
                response_text = result["response"]
            elif isinstance(result, str):
                response_text = str(result)
            else:
                response_text = str(result)

            return ChatOutput(
                response=response_text,
                thread_id=input_data.thread_id or "generated_thread_123",
                agent_id=input_data.agent_id,
                timestamp=datetime.now().isoformat(),
                context_used=len(input_data.knowledge_base_ids or []) > 0
            )

        except Exception as e:
            logger.error(f"智能体对话异常: {e}")
            return ChatOutput(
                response=f"对话服务异常: {e}",
                thread_id=input_data.thread_id or "error_thread",
                agent_id=input_data.agent_id,
                timestamp=datetime.now().isoformat(),
                context_used=False
            )

    async def create_knowledge_base_internal(self, input_data: KnowledgeBaseCreateInput) -> Dict[str, Any]:
        """创建知识库"""
        logger.info(f"执行MCP工具: yuxi-know-create-kb (name: {input_data.name})")

        try:
            data = {
                "name": input_data.name,
                "description": input_data.description,
                "kb_type": input_data.kb_type,
                "embedding_model": input_data.embedding_model
            }

            if input_data.llm_info:
                data["llm_info"] = input_data.llm_info

            result = await self._make_request("POST", "/knowledge/databases", json=data)

            if "error" in result:
                return {
                    "error": result["error"],
                    "status_code": result.get("status_code", 500),
                    "success": False
                }

            return {
                "database_id": result.get("id"),
                "name": result.get("name"),
                "status": result.get("status", "created"),
                "message": result.get("message", "知识库创建成功"),
                "success": True
            }

        except Exception as e:
            logger.error(f"创建知识库异常: {e}")
            return {
                "error": f"创建知识库失败: {e}",
                "status_code": 500,
                "success": False
            }

    async def get_available_knowledge_bases(self) -> AvailableKnowledgeBasesOutput:
        """获取知识库列表"""
        logger.info("执行MCP工具: yuxi-know-get-kbs")

        try:
            result = await self._make_request("GET", "/knowledge/databases")

            if "error" in result:
                return AvailableKnowledgeBasesOutput(
                    databases=[],
                    total_count=0,
                    active_count=0
                )

            databases = result.get("data", {}).get("databases", [])

            return AvailableKnowledgeBasesOutput(
                databases=databases,
                total_count=len(databases),
                active_count=len([db for db in databases if db.get("status") == "active"])
            )

        except Exception as e:
            logger.error(f"获取知识库列表异常: {e}")
            return AvailableKnowledgeBasesOutput(
                databases=[],
                total_count=0,
                active_count=0
            )

    async def search_graph_entities(self, input_data: EntitySearchInput) -> EntitySearchOutput:
        """搜索图谱实体"""
        logger.info(f"执行MCP工具: yuxi-know-search-entities (entity: {input_data.entity_name})")

        try:
            params = {
                "entity_name": input_data.entity_name,
                "limit": input_data.limit
            }

            result = await self._make_request("GET", "/graph/neo4j/node", params=params)

            if "error" in result:
                return EntitySearchOutput(
                    entities=[],
                    relationships=[],
                    total_count=0
                )

            # 处理后端API返回的格式: {"success": True, "result": {...}, "message": "success"}
            if result.get("success") and "result" in result:
                graph_result = result["result"]

                if isinstance(graph_result, dict):
                    # 优先使用 nodes 和 edges 字段
                    entities = graph_result.get("nodes", [])
                    relationships = graph_result.get("edges", [])

                    # 如果没有 nodes/edges，尝试其他字段名
                    if not entities:
                        entities = graph_result.get("entities", [])
                    if not relationships:
                        relationships = graph_result.get("relationships", [])

                elif isinstance(graph_result, list):
                    # 如果是列表，假设是实体列表
                    entities = graph_result
                    relationships = []
                else:
                    entities = []
                    relationships = []
            else:
                # 兼容其他格式
                entities = result.get("entities", [])
                relationships = result.get("relationships", [])

            # 限制返回的数据量
            if len(entities) > 50:
                entities = entities[:50]
                logger.warning(f"实体数量过多，截取前50个")
            if len(relationships) > 50:
                relationships = relationships[:50]
                logger.warning(f"关系数量过多，截取前50个")

            logger.info(f"MCP实体搜索: 找到 {len(entities)} 个节点，{len(relationships)} 个边")

            # 基本数据清理，确保可序列化
            cleaned_entities = []
            cleaned_relationships = []

            for entity in entities:
                if isinstance(entity, dict):
                    clean_entity = {}
                    for key, value in entity.items():
                        # 只保留基本类型
                        if isinstance(value, (str, int, float, bool)):
                            clean_entity[key] = value
                        elif value is None:
                            clean_entity[key] = None
                        else:
                            clean_entity[key] = str(value)
                    cleaned_entities.append(clean_entity)

            for rel in relationships:
                if isinstance(rel, dict):
                    clean_rel = {}
                    for key, value in rel.items():
                        if isinstance(value, (str, int, float, bool)):
                            clean_rel[key] = value
                        elif value is None:
                            clean_rel[key] = None
                        else:
                            clean_rel[key] = str(value)
                    cleaned_relationships.append(clean_rel)

            return EntitySearchOutput(
                entities=cleaned_entities,
                relationships=cleaned_relationships,
                total_count=len(cleaned_entities)
            )

        except Exception as e:
            logger.error(f"实体搜索异常: {e}")
            return EntitySearchOutput(
                entities=[],
                relationships=[],
                total_count=0
            )

    async def get_system_tools(self) -> ToolListOutput:
        """获取可用工具列表"""
        logger.info("执行MCP工具: yuxi-know-get-tools")

        try:
            result = await self._make_request("GET", "/tool/tools")

            if "error" in result:
                return ToolListOutput(
                    tools=[],
                    categories=[],
                    total_count=0
                )

            tools = result.get("tools", [])
            categories = list(set(tool.get("category") for tool in tools if tool.get("category")))

            return ToolListOutput(
                tools=tools,
                categories=categories,
                total_count=len(tools)
            )

        except Exception as e:
            logger.error(f"获取工具列表异常: {e}")
            return ToolListOutput(
                tools=[],
                categories=[],
                total_count=0
            )

    async def get_graph_statistics_info(self) -> GraphStatsOutput:
        """获取图谱统计信息"""
        logger.info("执行MCP工具: yuxi-know-graph-stats")

        try:
            result = await self._make_request("GET", "/graph/lightrag/stats")

            if "error" in result:
                return GraphStatsOutput(
                    total_nodes=0,
                    total_edges=0,
                    entity_types={},
                    last_updated=datetime.now().isoformat()
                )

            return GraphStatsOutput(
                total_nodes=result.get("total_nodes", 0),
                total_edges=result.get("total_edges", 0),
                entity_types=result.get("entity_types", {}),
                last_updated=result.get("last_updated", datetime.now().isoformat())
            )

        except Exception as e:
            logger.error(f"获取图谱统计异常: {e}")
            return GraphStatsOutput(
                total_nodes=0,
                total_edges=0,
                entity_types={},
                last_updated=datetime.now().isoformat()
            )

    async def get_entity_relations(self, input_data):
        """获取实体关系，返回自然语言描述"""
        logger.info(f"执行MCP工具: yuxi-know-entity-relations (entity: {input_data.entity_name})")

        try:
            params = {
                "entity_name": input_data.entity_name,
                "limit": input_data.limit
            }

            # 调用现有的实体搜索API
            result = await self._make_request("GET", "/graph/neo4j/node", params=params)

            if "error" in result:
                from server.mcp.yuxi_know_tools import EntityRelationsOutput
                return EntityRelationsOutput(
                    entity_name=input_data.entity_name,
                    relations_summary=f"未找到实体'{input_data.entity_name}'的关系信息",
                    detailed_relations=[],
                    total_relations=0
                )

            # 提取图数据
            graph_result = result.get("result", {})
            entities = graph_result.get("nodes", [])
            relationships = graph_result.get("edges", [])

            # 转换为自然语言关系描述
            relations_description = self._convert_relations_to_natural_language(
                entities, relationships, input_data.entity_name
            )

            from server.mcp.yuxi_know_tools import EntityRelationsOutput
            return EntityRelationsOutput(
                entity_name=input_data.entity_name,
                relations_summary=relations_description["summary"],
                detailed_relations=relations_description["detailed_relations"],
                total_relations=len(relations_description["detailed_relations"])
            )

        except Exception as e:
            logger.error(f"获取实体关系异常: {e}")
            from server.mcp.yuxi_know_tools import EntityRelationsOutput
            return EntityRelationsOutput(
                entity_name=input_data.entity_name,
                relations_summary=f"查询实体关系时发生错误: {str(e)}",
                detailed_relations=[],
                total_relations=0
            )

    def _convert_relations_to_natural_language(self, entities, relationships, target_entity):
        """将图数据转换为自然语言关系描述"""

        # 创建实体名称映射
        entity_name_map = {}
        for entity in entities:
            entity_id = entity.get('id', '')
            entity_name = entity.get('name', '')
            if entity_id and entity_name:
                entity_name_map[entity_id] = entity_name

        # 过滤与目标实体相关的关系
        target_relations = []
        for rel in relationships:
            source_id = rel.get('source_id', '')
            target_id = rel.get('target_id', '')
            rel_type = rel.get('type', '')

            source_name = entity_name_map.get(source_id, '')
            target_name = entity_name_map.get(target_id, '')

            # 检查是否与目标实体相关
            if (source_name == target_entity or target_name == target_entity) and source_name and target_name:
                # 构建自然语言描述
                if source_name == target_entity:
                    relation_desc = self._build_relation_description(source_name, rel_type, target_name, "outgoing")
                else:
                    relation_desc = self._build_relation_description(target_name, rel_type, source_name, "incoming")

                if relation_desc:
                    target_relations.append(relation_desc)

        # 去重并排序
        limit = 20  # 默认限制20个关系
        unique_relations = list(set(target_relations))[:limit]

        # 生成汇总描述
        if not unique_relations:
            summary = f"未找到实体'{target_entity}'的关系信息"
        else:
            summary = f"实体'{target_entity}'共有{len(unique_relations)}个关系："

            if len(unique_relations) <= 3:
                summary += "、".join(unique_relations) + "。"
            else:
                summary += "、".join(unique_relations[:3]) + f"等{len(unique_relations)}个关系。"

        return {
            "summary": summary,
            "detailed_relations": unique_relations
        }

    def _build_relation_description(self, entity1, relation_type, entity2, direction):
        """构建自然语言关系描述"""

        # 定义关系类型到自然语言的映射
        relation_map = {
            "夫": "是{entity2}的丈夫",
            "妻": "是{entity2}的妻子",
            "子": "是{entity2}的儿子",
            "女": "是{entity2}的女儿",
            "父": "是{entity2}的父亲",
            "母": "是{entity2}的母亲",
            "兄": "是{entity2}的哥哥",
            "弟": "是{entity2}的弟弟",
            "姐": "是{entity2}的姐姐",
            "妹": "是{entity2}的妹妹",
            "祖父": "是{entity2}的祖父",
            "祖母": "是{entity2}的祖母",
            "孙": "是{entity2}的孙子",
            "孙女": "是{entity2}的孙女",
            "门生": "是{entity2}的门生",
            "朋友": "是{entity2}的朋友",
            "同事": "是{entity2}的同事"
        }

        # 尝试使用预定义映射
        if relation_type in relation_map:
            return relation_map[relation_type].format(entity2=entity2)

        # 对于传入关系，反转描述
        if direction == "incoming":
            # entity1是关系的目标，entity2是关系的源
            if relation_type in relation_map:
                template = relation_map[relation_type].replace("是{entity2}的", "{entity2}的").replace("{entity2}", "{entity1}")
                return template.format(entity1=entity1, entity2=entity2)

        # 默认描述
        return f"{entity1}与{entity2}有{relation_type}关系"


# 全局MCP服务器实例
_yuxi_know_mcp_server: Optional[YuxiKnowMCPServer] = None


def get_mcp_server() -> YuxiKnowMCPServer:
    """获取MCP服务器实例"""
    global _yuxi_know_mcp_server
    if _yuxi_know_mcp_server is None:
        _yuxi_know_mcp_server = YuxiKnowMCPServer()
    return _yuxi_know_mcp_server


def set_mcp_server_config(api_base_url: str = None, api_key: str = None):
    """设置MCP服务器配置"""
    global _yuxi_know_mcp_server
    _yuxi_know_mcp_server = YuxiKnowMCPServer(api_base_url, api_key)